﻿/*
   Copyright 2011 Andrew Davis

    This file is part of Base.

    Base is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Base is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Base.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Base.MySql
{
	using System;
	using System.Reflection;
	using System.Text.RegularExpressions;
	using global::MySql.Data.MySqlClient;
	using Helpers;
	using System.Diagnostics;

	/// <summary>
	/// A strongly type list of a object that derives from the BaseClass
	/// </summary>
	/// <typeparam name="T">An object that is derived from the BaseClass</typeparam>
	[Serializable()]
	public class BaseList<T> : Prototypes.BaseList<T> where T : BaseClass
	{
		/// <summary>
		/// Reads data from the database and constructs an object based on that information.
		/// The data coming from the query must be name the same the property of the class
		/// </summary>
		/// <param name="connStr">The connection string to the data source</param>
		/// <param name="sqlText">The SQL to run</param>
		/// <param name="listType">The object type to contained in the list</param>
		/// <param name="objectType">The object type to be returned</param>
		/// <param name="commandType">The type of the command to run</param>
		/// <param name="additionalParams">A loosly typed object containing the parameters needed the the SQL</param>
		/// <returns>An object derived from the BaseClass</returns>
		protected static BaseList<T> Read(string connStr, string sqlText, Type listType, Type objectType, System.Data.CommandType commandType = System.Data.CommandType.Text, object additionalParams = null)
		{
			BaseList<T> list = null;
			using (MySqlConnection conn = new MySqlConnection(connStr))
			{
				try
				{
					using (MySqlCommand comm = new MySqlCommand(sqlText, conn))
					{
						comm.CommandType = commandType;
						switch (comm.CommandType)
						{
							case System.Data.CommandType.StoredProcedure:
								conn.Open();
								MySqlCommandBuilder.DeriveParameters(comm);
								conn.Close();
								break;
							case System.Data.CommandType.Text:
								// PARSE THE SQL WITH REGEX LOOKING FOR THE PARAMS
								string sqlQuery = comm.CommandText;
								MatchCollection matchCollection = Regex.Matches(sqlQuery, @"(?<Parameter>@\w*)");
								MySqlParameter sqlParam;
								foreach (Match match in matchCollection)
								{
									sqlParam = comm.CreateParameter();
									sqlParam.ParameterName = match.Value;
									comm.Parameters.Add(sqlParam);
								}
								break;
						}
						if (additionalParams != null)
						{
							foreach (PropertyInfo propertyInfo in additionalParams.GetType().GetProperties())
								foreach (MySqlParameter param in comm.Parameters)
									if (("@" + propertyInfo.Name).ToLower() == param.ParameterName.ToLower())
										param.Value = propertyInfo.GetValue(additionalParams, null);
						}
						conn.Open();
						MySqlDataReader dataRdr = comm.ExecuteReader();
						if (dataRdr.HasRows)
						{
							ConstructorInfo cI = listType.GetConstructor(Type.EmptyTypes);
							list = cI.Invoke(new object[] { }) as BaseList<T>;
							BaseClass bc;
							while (dataRdr.Read())
							{
								cI = objectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(MySqlDataReader) }, null);
								bc = cI.Invoke(new object[] { dataRdr }) as BaseClass;
								list.Add(bc as T);
							}
						}
						return list;
					}
				}
				catch (Exception ex)
				{
					Log.LogError(ex, true, true, true);
					Debug.WriteLine("Message: " + ex.Message);
					Debug.WriteLine("Inner Exception: " + ex.InnerException);
					return list;
				}
				finally
				{
					conn.Close();
				}
			}
		}
	}
}
